home *** CD-ROM | disk | FTP | other *** search
/ Enigma Amiga Life 109 / EnigmaAmiga109CD.iso / dalla rivista / amiga.free / sorgenti vari / wolfedit2 2.0.4 source.sit / WolfEdit2 2.0.4 Source / UCreateLevel.p < prev    next >
Text File  |  1995-12-07  |  13KB  |  506 lines

  1. unit UCreateLevel;
  2.  
  3. interface
  4.     uses
  5.         ULevel, UWolfDoc;
  6.  
  7.     type
  8.  
  9.         ObjectCountRecord = record
  10.                 numObjects: integer;
  11.                 objectBytes: longint;
  12.                 numGuards: integer;
  13.                 numTreasures: integer;
  14.                 numDoors: integer;
  15.                 numSecretDoors: integer;
  16.                 numRooms: integer;
  17.                 numSprites: integer;
  18.                 numSpritesInRoom: array[0..63] of integer;
  19.             end;
  20.  
  21.     function CreateLevelFromMap (map: TMapCells; var h: LevelHandle; name: string; checkLimits: boolean): OSErr;
  22.     procedure CalculateStatistics (map: TMapCells; var stats: ObjectCountRecord);
  23.  
  24. implementation
  25.     uses
  26.         UBSP;
  27.  
  28.     const
  29.  
  30.         noRequiredItemsAlrtID = 132;
  31.         tooManyRoomsAlrtID = 134;
  32.         tooManyThingsAlrtID = 138;
  33.  
  34.         soundAreaMarkerFlag = 1;
  35.  
  36. {$D-}
  37. {$R-}
  38.  
  39.     function CheckNonzeroCounts (counts: ObjectCountRecord; name: string): OSErr;
  40.         var
  41.             n: integer;
  42.             s: string;
  43.  
  44.         procedure Check (count: integer; what: string);
  45.         begin
  46.             if count = 0 then begin
  47.                     case n of
  48.                         0: 
  49.                             s := what;
  50.                         1: 
  51.                             s := concat(what, ' or ', s);
  52.                         otherwise
  53.                             s := concat(what, ', ', s);
  54.                     end;
  55.                     n := n + 1;
  56.                 end;
  57.         end;
  58.  
  59.     begin
  60.         with counts do begin
  61.                 n := 0;
  62.                 s := '';
  63.                 Check(numSecretDoors, 'secret doors');
  64.                 Check(numTreasures, 'treasures');
  65.                 Check(numGuards, 'guards');
  66.                 if n > 0 then begin
  67.                         ParamText(name, s, '', '');
  68.                         if Ask(noRequiredItemsAlrtID) = ok then
  69.                             CheckNonzeroCounts := noErr
  70.                         else
  71.                             CheckNonzeroCounts := suppressErr;
  72.                     end
  73.                 else
  74.                     CheckNonzeroCounts := noErr;
  75.             end;
  76.     end;
  77.  
  78. {$PUSH}
  79. {$D+}
  80.     function CheckCountLimits (counts: ObjectCountRecord; name: string; checkSprites: boolean): OSErr;
  81.         var
  82.             i: integer;
  83.  
  84.         procedure Check (count, limit: integer; what: string);
  85.         begin
  86.             if count > limit then begin
  87.                     ParamText(name, what, StringOf(limit : 1), StringOf(count : 1));
  88.                     if Ask(tooManyThingsAlrtID) = cancel then begin
  89.                             CheckCountLimits := suppressErr;
  90.                             exit(CheckCountLimits);
  91.                         end;
  92.                 end;
  93.         end;
  94.  
  95.     begin
  96.         with counts do begin
  97.                 Check(numSecretDoors, 64, 'too many secret doors');
  98.                 Check(numDoors, 64, 'too many doors');
  99.                 if checkSprites then begin
  100.                         Check(numSprites - numGuards, 200, 'too many objects');
  101.                         Check(numGuards, 127, 'too many guards');
  102.                         for i := 0 to 63 do
  103.                             Check(numSpritesInRoom[i], 64, 'a room with too many objects or guards');
  104.                     end;
  105.             end;
  106.         CheckCountLimits := noErr;
  107.     end;
  108. {$D-}
  109.  
  110.     procedure CountObjects (var map: MapCellGrid; var info: ObjectCountRecord);
  111.         var
  112.             row, col, room, i: integer;
  113.             code: MapCell;
  114.     begin
  115.         with info do begin
  116.                 numObjects := 0;
  117.                 objectBytes := 0;
  118.                 numGuards := 0;
  119.                 numTreasures := 0;
  120.                 numDoors := 0;
  121.                 numSecretDoors := 0;
  122.                 numSprites := 0;
  123.                 for i := 0 to 63 do
  124.                     numSpritesInRoom[i] := 0;
  125.                 for row := 0 to 63 do
  126.                     for col := 0 to 63 do begin
  127.                             code := map[row, col];
  128.                             if IsObject(code) then begin
  129.                                     numObjects := numObjects + 1;
  130.                                     objectBytes := objectBytes + 3;
  131.                                     room := map[row, col].area;
  132.                                     if IsDoor(code) then
  133.                                         numDoors := numDoors + 1
  134.                                     else if IsSecretDoor(code) then begin
  135.                                             objectBytes := objectBytes + 1;
  136.                                             numSecretDoors := numSecretDoors + 1;
  137.                                         end
  138.                                     else if code.obj >= firstObjectCode then begin
  139.                                             numSprites := numSprites + 1;
  140.                                             if (room >= 0) & (room <= 63) then
  141.                                                 numSpritesInRoom[room] := numSpritesInRoom[room] + 1;
  142.                                             if IsGuard(code) then
  143.                                                 numGuards := numGuards + 1
  144.                                             else if IsTreasure(code) then
  145.                                                 numTreasures := numTreasures + 1;
  146.                                         end;
  147.                                 end;
  148.                             if (code.wall >= 1) and (code.wall <= 64) then {sound area marker}
  149.                                 objectBytes := objectBytes + 3;
  150.                             if code.missingQuarters <> 0 then {quartering marker}
  151.                                 objectBytes := objectBytes + 3;
  152.                         end;
  153.             end;
  154.     end;
  155.  
  156. {$PUSH}
  157. {$D+}
  158. {$R+}
  159.     procedure FillRoom (var map: MapCellGrid; row, col, room, inMask: integer);
  160.         var
  161.             code: MapCell;
  162.             mq: integer;
  163.     begin
  164.         with map[row, col] do
  165.             if area = $7F then
  166.                 if (wall < $80) or (obj = $62) then begin
  167.                         area := room;
  168.                         if row > 0 then
  169.                             FillRoom(map, row - 1, col, room, $C);
  170.                         if row < 63 then
  171.                             FillRoom(map, row + 1, col, room, $3);
  172.                         if col > 0 then
  173.                             FillRoom(map, row, col - 1, room, $A);
  174.                         if col < 63 then
  175.                             FillRoom(map, row, col + 1, room, $5);
  176.                     end
  177.                 else if BAND(missingQuarters, inMask) <> 0 then begin
  178.                         area := room;
  179.                         mq := missingQuarters;
  180.                         if (row > 0) and (BAND(mq, $3) <> 0) then
  181.                             FillRoom(map, row - 1, col, room, BAND($C, BSL(mq, 2)));
  182.                         if (row < 63) and (BAND(mq, $C) <> 0) then
  183.                             FillRoom(map, row + 1, col, room, BAND($3, BSR(mq, 2)));
  184.                         if (col > 0) and (BAND(mq, $5) <> 0) then
  185.                             FillRoom(map, row, col - 1, room, BAND($A, BSL(mq, 1)));
  186.                         if (col < 63) and (BAND(mq, $A) <> 0) then
  187.                             FillRoom(map, row, col + 1, room, BAND($5, BSR(mq, 1)));
  188.                     end;
  189.     end;
  190. {$POP}
  191.  
  192. {CalculateRooms allocates room numbers and set the area field of each cell to the}
  193. {room number of the room to which it belongs, or $40 if it does not belong to a room.}
  194. {It converts each sound area marker by moving the sound area number into the wall field.}
  195.  
  196. {$PUSH}
  197. {$D+}
  198. {$R+}
  199.     function CalculateRooms (var map: MapCellGrid): OSErr;
  200.         var
  201.             row, col: integer;
  202.             nextRoom: integer;
  203.             code: MapCell;
  204.     begin
  205.         CalculateRooms := noErr;
  206.     {Mark empty space with $7F and solid space with $40}
  207.         for row := 0 to 63 do
  208.             for col := 0 to 63 do
  209.                 with map[row, col] do begin
  210.                         if area > 0 then
  211.                             wall := area;
  212.                         if (wall < $80) or (obj = $62) or (missingQuarters <> 0) then
  213.                             area := $7F
  214.                         else
  215.                             area := $40;
  216.                     end;
  217.     {Flood-fill each contiguous $7F region with a unique room number}
  218.         nextRoom := 0;
  219.         for row := 0 to 63 do
  220.             for col := 0 to 63 do begin
  221.                     if map[row, col].area = $7F then begin
  222.                             FillRoom(map, row, col, BAND(nextRoom, $3F), $F);
  223.                             nextRoom := nextRoom + 1;
  224.                         end;
  225.                 end;
  226.         CalculateRooms := nextRoom;
  227.     end;
  228. {$D-}
  229.  
  230. {$PUSH}
  231. {$D+}
  232.     function CalcAndCheckRooms (var map: MapCellGrid; name: string): OSErr;
  233.         var
  234.             numRooms: integer;
  235.     begin
  236.         CalcAndCheckRooms := noErr;
  237.         numRooms := CalculateRooms(map);
  238.         if numRooms > 64 then begin
  239.                 ParamText(name, StringOf(numRooms : 1), '', '');
  240.                 if Ask(tooManyRoomsAlrtID) = cancel then
  241.                     CalcAndCheckRooms := suppressErr;
  242.             end;
  243.     end;
  244. {$POP}
  245.  
  246.     procedure PutWallArray (h: LevelHandle; var map: MapCellGrid);
  247.         var
  248.             row, col, item: integer;
  249.     begin
  250.         for row := 0 to 63 do
  251.             for col := 0 to 63 do begin
  252.                     with map[row, col] do begin
  253.                             if (wall >= $80) and (obj <> $62) then
  254.                                 item := wall
  255.                             else
  256.                                 item := area;
  257.                         end;
  258.                     h^^.map[row, col] := item;
  259.                 end;
  260.     end;
  261.  
  262. {For each room containing a sound marker, give it the sound area}
  263. {number of its marker. Then assign unused sound area numbers to the}
  264. {remaining rooms.}
  265.  
  266.     procedure PutSoundAreaTable (h: LevelHandle; var map: MapCellGrid);
  267.         type
  268.             Set64 = set of 0..63;
  269.         var
  270.             roomsDone, areasUsed: Set64;
  271.             row, col, room, area: integer;
  272.     begin
  273.         roomsDone := [];
  274.         areasUsed := [];
  275.         for row := 0 to 63 do
  276.             for col := 0 to 63 do begin
  277.                     area := map[row, col].wall;
  278.                     if (area >= 1) & (area <= 64) then begin
  279.                             area := area - 1;
  280.                             room := map[row, col].area;
  281.                             if room <= 63 then begin
  282.                                     h^^.zones[room] := area;
  283.                                     roomsDone := roomsDone + [room];
  284.                                     areasUsed := areasUsed + [area];
  285.                                 end;
  286.                         end;
  287.                 end;
  288.         area := 0;
  289.         for room := 0 to 63 do
  290.             if not (room in roomsDone) then begin
  291.                     while (area < 64) & (area in areasUsed) do
  292.                         area := area + 1;
  293.                     if area < 64 then begin
  294.                             areasUsed := areasUsed + [area];
  295.                             h^^.zones[room] := area;
  296.                         end
  297.                     else {shouldn't happen, but just in case}
  298.                         h^^.zones[room] := 0;
  299.                 end;
  300.     end;
  301.  
  302.     procedure PutOffsetTable (h: LevelHandle; numObjects: integer; objectBytes: longint; numBSPEntries: integer);
  303.         var
  304.             objOffset, bspOffset: longint;
  305.     begin
  306.         objOffset := 64 * 64 + 64 + 8;
  307.         bspOffset := objOffset + objectBytes;
  308.         SetLittleEndian(h^^.numObjects, numObjects);
  309.         SetLittleEndian(h^^.objOffset, objOffset);
  310.         SetLittleEndian(h^^.numBSPEntries, numBSPEntries);
  311.         SetLittleEndian(h^^.bspOffset, bspOffset);
  312.     end;
  313.  
  314.     procedure PutObjectTable (h: LevelHandle; var map: MapCellGrid);
  315.         var
  316.             obj: ObjectEntry;
  317.             p: longint;
  318.             row, col: integer;
  319.             code: MapCell;
  320.     begin
  321.         p := 0;
  322.         for row := 0 to 63 do
  323.             for col := 0 to 63 do begin
  324.                     code := map[row, col];
  325.                     obj.x := col;
  326.                     obj.y := row;
  327.                     if IsObject(code) then begin
  328.                             obj.code := ExtractObject(code);
  329.                             if obj.code = $62 then
  330.                                 obj.code2 := ExtractObjectExtra(code);
  331.                             PutObject(h, p, obj);
  332.                         end;
  333.                     if (code.wall >= 1) and (code.wall <= 64) then begin {sound area marker}
  334.                             obj.code := $FF;
  335.                             PutObject(h, p, obj);
  336.                         end;
  337.                     if code.missingQuarters <> 0 then begin
  338.                             obj.code := $E0 + code.missingQuarters;
  339.                             PutObject(h, p, obj);
  340.                         end;
  341.                 end;
  342.     end;
  343.  
  344.     procedure NumberBSPEntries (tree: BSPTreePtr; var n: integer);
  345.  
  346.         procedure Number (var i: integer);
  347.         begin
  348.             i := n;
  349.             n := n + 1;
  350.         end;
  351.  
  352.         procedure NumberTree (p: BSPTreePtr);
  353.             var
  354.                 seg: SegmentPtr;
  355.         begin
  356.             case p^.kind of
  357.                 nonterminal:  begin
  358.                         Number(p^.entry);
  359.                         NumberTree(p^.links[0]);
  360.                         NumberTree(p^.links[1]);
  361.                     end;
  362.                 terminal:  begin
  363.                         p^.entry := n;
  364.                         seg := p^.segments;
  365.                         while seg <> nil do begin
  366.                                 Number(seg^.entry);
  367.                                 seg := seg^.next;
  368.                             end;
  369.                     end;
  370.             end;
  371.         end;
  372.  
  373.     begin {NumberBSPEntries}
  374.         n := 0;
  375.         NumberTree(tree);
  376.     end;
  377.  
  378.     procedure PutNonterminal (lh: LevelHandle; p: BSPTreePtr);
  379.         var
  380.             n, i: integer;
  381.             e: BSPEntry;
  382.             w: LittleEndianWord;
  383.     begin
  384.         n := p^.entry;
  385.         e.coord0 := p^.splitCoord;
  386.         case p^.splitDir of
  387.             h: 
  388.                 e.flags := bspSplitH;
  389.             v: 
  390.                 e.flags := bspSplitV;
  391.         end;
  392.         for i := 0 to 1 do begin
  393.                 SetLittleEndian(w, p^.links[i]^.entry);
  394.                 e.links[i] := w;
  395.             end;
  396.         PutBSPEntry(lh, n, e);
  397.     end;
  398.  
  399.     procedure PutSegment (lh: LevelHandle; p: SegmentPtr; lastSeg: boolean);
  400.         var
  401.             n, code: integer;
  402.             e: BSPEntry;
  403.     begin
  404.         n := p^.entry;
  405.         e.coord0 := p^.pos;
  406.         case p^.dir of
  407.             h: 
  408.                 case p^.face of
  409.                     nw: 
  410.                         code := bspFaceNorth;
  411.                     se: 
  412.                         code := bspFaceSouth;
  413.                 end;
  414.             v: 
  415.                 case p^.face of
  416.                     nw: 
  417.                         code := bspFaceWest;
  418.                     se: 
  419.                         code := bspFaceEast;
  420.                 end;
  421.         end;
  422.         if lastSeg then
  423.             code := code + bspLastSeg;
  424.         e.flags := bspTerminal + code;
  425.         e.coord1 := p^.ends[0];
  426.         e.coord2 := p^.ends[1];
  427.         e.grid := p^.grid;
  428.         e.area := p^.area;
  429.         PutBSPEntry(lh, n, e);
  430.     end;
  431.  
  432.     procedure PutBSPTree (h: LevelHandle; p: BSPTreePtr);
  433.         var
  434.             seg: SegmentPtr;
  435.     begin
  436.         case p^.kind of
  437.             nonterminal:  begin
  438.                     PutNonterminal(h, p);
  439.                     PutBSPTree(h, p^.links[0]);
  440.                     PutBSPTree(h, p^.links[1]);
  441.                 end;
  442.             terminal:  begin
  443.                     seg := p^.segments;
  444.                     while seg <> nil do begin
  445.                             PutSegment(h, seg, seg^.next = nil);
  446.                             seg := seg^.next;
  447.                         end;
  448.                 end;
  449.         end;
  450.     end;
  451.  
  452.     function CreateLevelFromMap (map: TMapCells; var h: LevelHandle; name: string; checkLimits: boolean): OSErr;
  453.         var
  454.             numObjects, numBSPEntries: integer;
  455.             objectBytes, bspBytes, totBytes: longint;
  456.             tree: BSPTreePtr;
  457.             grid: MapCellGrid;
  458.             counts: ObjectCountRecord;
  459.  
  460.         procedure Check (result: OSErr);
  461.         begin
  462.             if result <> noErr then begin
  463.                     DisposeBSPTree(tree);
  464.                     if h <> nil then
  465.                         DisposeLevel(h);
  466.                     CreateLevelFromMap := result;
  467.                     exit(CreateLevelFromMap);
  468.                 end;
  469.         end;
  470.  
  471. {$D+}
  472.     begin
  473.         h := nil;
  474.         tree := nil;
  475.         map.CopyToGrid(grid);
  476.         Check(CalcAndCheckRooms(grid, name));
  477.         CountObjects(grid, counts);
  478.         Check(CheckNonzeroCounts(counts, name));
  479.         Check(CheckCountLimits(counts, name, checkLimits));
  480.         Check(CreateBSPTree(grid, tree));
  481.         NumberBSPEntries(tree, numBSPEntries);
  482.         bspBytes := 6 * numBSPEntries;
  483.         totBytes := 64 * 64 + 64 + 8 + counts.objectBytes + bspBytes;
  484.         h := LevelHandle(NewHandle(totBytes));
  485.         Check(MemError);
  486.         PutWallArray(h, grid);
  487.         PutSoundAreaTable(h, grid);
  488.         with counts do
  489.             PutOffsetTable(h, (objectBytes - numSecretDoors) div 3, objectBytes, numBSPEntries);
  490.         PutObjectTable(h, grid);
  491.         PutBSPTree(h, tree);
  492.         DisposeBSPTree(tree);
  493.         CreateLevelFromMap := noErr;
  494.     end;
  495.  
  496.     procedure CalculateStatistics (map: TMapCells; var stats: ObjectCountRecord);
  497.         var
  498.             grid: MapCellGrid;
  499.             result: OSErr;
  500.     begin
  501.         map.CopyToGrid(grid);
  502.         stats.numRooms := CalculateRooms(grid);
  503.         CountObjects(grid, stats);
  504.     end;
  505.  
  506. end.